/**
 * 
 */
package br.com.framework.apresentacao.bean;

import java.io.Serializable;
import java.util.Iterator;

import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.faces.component.UIComponent;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;

import br.com.framework.util.resource.MessageResource;
import br.com.framework.util.resource.MessageResourceFactory;

/**
 * @author Cleber
 * 
 */
public abstract class BaseBean implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	protected static final Logger LOGGER = Logger.getLogger(BaseBean.class);

	public static final String DEFAULT_RESOURCE_BUNDLE = "mensagens";

	protected MessageResource messageResource;

	/**
	 * 
	 */
	public BaseBean() {
		super();
		this.messageResource = MessageResourceFactory.createMessageResource(
				DEFAULT_RESOURCE_BUNDLE, getClass().getClassLoader());
	}

	/**
	 * Recupera a instância do {@link FacesContext}.
	 * 
	 * @return
	 */
	protected FacesContext getFacesContext() {
		return FacesContext.getCurrentInstance();
	}

	/**
	 * Recupera a instância do {@link ExternalContext}.
	 * 
	 * @return
	 */
	protected ExternalContext getExternalContext() {
		return getFacesContext().getExternalContext();
	}

	/**
	 * Recupera a instância do bean da sessão.
	 * 
	 * @return O HttpSession recuperado.
	 */
	public HttpSession getHttpSession() {
		return (HttpSession) getExternalContext().getSession(true);
	}

	/**
	 * Recupera um bean armazenado na sessão.
	 * 
	 * @param bean
	 *            O nome do bean a ser recuperado.
	 * @return O bean recuperado.
	 */
	public Object getBeanFromSession(String bean) {
		return getExternalContext().getSessionMap().get(bean);
	}

	/**
	 * Recupera um bean armazenado na requisição.
	 * 
	 * @param bean
	 *            O nome do bean a ser recuperado.
	 * @return O bean recuperado.
	 */
	public Object getBeanFromRequest(String bean) {
		return getExternalContext().getRequestMap().get(bean);
	}

	/**
	 * Adiciona uma mensagem ao {@link FacesContext}
	 * 
	 * @param componentId 
	 * 	Id do componente ao qual a mensagem será associada.
	 * @param severity
	 * 	Severidade da mensagem do tipo {@link Severity}. Ex: FacesMessage.SEVERITY_INFO, FacesMessage.SEVERITY_WARN, etc.
	 * @param msgSummaryKey
	 * 	Chave da mensagem a ser utilizada.
	 * @param msgDetailKey
	 * 	Chave do detalhamento a ser utilizado
	 * @param msgSummaryArgs
	 * 	Argumentos da mensagem
	 * @param msgDetailArgs
	 * 	Argumentos do detalhamento.
	 */
	protected void addMessage(String componentId, Severity severity,
			String msgSummaryKey, String msgDetailKey, Object[] msgSummaryArgs,
			Object[] msgDetailArgs) {
		FacesMessage facesMessage = new FacesMessage();
		facesMessage.setSeverity(severity);
		if (msgSummaryKey != null) {
			if (msgSummaryArgs != null && msgSummaryArgs.length > 0) {
				facesMessage.setSummary(this.messageResource.get(msgSummaryKey,
						msgSummaryArgs));
			} else {
				facesMessage
						.setSummary(this.messageResource.get(msgSummaryKey));
			}
		}
		if (msgDetailKey != null) {
			if (msgDetailArgs != null && msgDetailArgs.length > 0) {
				facesMessage.setDetail(this.messageResource.get(msgDetailKey,
						msgDetailArgs));
			} else {
				facesMessage.setDetail(this.messageResource.get(msgDetailKey));
			}
		}
		String clientId = null;
		if (componentId != null) {
			UIComponent component = findComponent(getFacesContext()
					.getViewRoot(), componentId);
			clientId = component.getClientId(getFacesContext());
		}
		getFacesContext().addMessage(clientId, facesMessage);
	}
	
	/**
	 * Adiciona uma mensagem ao {@link FacesContext}
	 * 
	 * @param componentId 
	 * 	Id do componente ao qual a mensagem será associada.
	 * @param severity
	 * 	Severidade da mensagem do tipo {@link Severity}. Ex: FacesMessage.SEVERITY_INFO, FacesMessage.SEVERITY_WARN, etc.
	 * @param msgSummaryKey
	 * 	Chave da mensagem a ser utilizada.
	 * @param msgSummaryArgs
	 * 	Argumentos da mensagem
	 */
	protected void addMessage(String componentId, Severity severity,
			String msgSummaryKey, Object[] msgSummaryArgs) {
		addMessage(componentId, severity, msgSummaryKey, null, msgSummaryArgs, null);
	}
	
	/**
	 * Adiciona uma mensagem de severidade INFO ao {@link FacesContext}
	 * 
	 * @param componentId 
	 * 	Id do componente ao qual a mensagem será associada.
	 * @param msgSummaryKey
	 * 	Chave da mensagem a ser utilizada.
	 * @param msgDetailKey
	 * 	Chave do detalhamento a ser utilizado
	 * @param msgSummaryArgs
	 * 	Argumentos da mensagem
	 * @param msgDetailArgs
	 * 	Argumentos do detalhamento.
	 */
	protected void addMessageInfo(String componentId, String msgSummaryKey, String msgDetailKey, Object[] msgSummaryArgs,
			Object[] msgDetailArgs) {
		addMessage(componentId, FacesMessage.SEVERITY_INFO, msgSummaryKey, msgDetailKey, msgSummaryArgs, msgDetailArgs);
	}
	
	/**
	 * Adiciona uma mensagem de severidade INFO ao {@link FacesContext}
	 * 
	 * @param componentId 
	 * 	Id do componente ao qual a mensagem será associada.
	 * @param msgSummaryKey
	 * 	Chave da mensagem a ser utilizada.
	 * @param msgSummaryArgs
	 * 	Argumentos da mensagem
	 */
	protected void addMessageInfo(String componentId, String msgSummaryKey, Object... msgSummaryArgs) {
		addMessageInfo(componentId, msgSummaryKey, null, msgSummaryArgs, null);
	}
	
	/**
	 * Adiciona uma mensagem de severidade ERROR ao {@link FacesContext}
	 * 
	 * @param componentId 
	 * 	Id do componente ao qual a mensagem será associada.
	 * @param msgSummaryKey
	 * 	Chave da mensagem a ser utilizada.
	 * @param msgDetailKey
	 * 	Chave do detalhamento a ser utilizado
	 * @param msgSummaryArgs
	 * 	Argumentos da mensagem
	 * @param msgDetailArgs
	 * 	Argumentos do detalhamento.
	 */
	protected void addMessageError(String componentId, String msgSummaryKey, String msgDetailKey, Object[] msgSummaryArgs,
			Object[] msgDetailArgs) {
		addMessage(componentId, FacesMessage.SEVERITY_ERROR, msgSummaryKey, msgDetailKey, msgSummaryArgs, msgDetailArgs);
	}
	
	/**
	 * Adiciona uma mensagem de severidade ERROR ao {@link FacesContext}
	 * 
	 * @param componentId 
	 * 	Id do componente ao qual a mensagem será associada.
	 * @param msgSummaryKey
	 * 	Chave da mensagem a ser utilizada.
	 * @param msgSummaryArgs
	 * 	Argumentos da mensagem
	 */
	protected void addMessageError(String componentId, String msgSummaryKey, Object... msgSummaryArgs) {
		addMessageError(componentId, msgSummaryKey, null, msgSummaryArgs, null);
	}
	
	/**
	 * Adiciona uma mensagem de severidade WARN ao {@link FacesContext}
	 * 
	 * @param componentId 
	 * 	Id do componente ao qual a mensagem será associada.
	 * @param msgSummaryKey
	 * 	Chave da mensagem a ser utilizada.
	 * @param msgDetailKey
	 * 	Chave do detalhamento a ser utilizado
	 * @param msgSummaryArgs
	 * 	Argumentos da mensagem
	 * @param msgDetailArgs
	 * 	Argumentos do detalhamento.
	 */
	protected void addMessageWarn(String componentId, String msgSummaryKey, String msgDetailKey, Object[] msgSummaryArgs,
			Object[] msgDetailArgs) {
		addMessage(componentId, FacesMessage.SEVERITY_WARN, msgSummaryKey, msgDetailKey, msgSummaryArgs, msgDetailArgs);
	}
	
	/**
	 * Adiciona uma mensagem de severidade WARN ao {@link FacesContext}
	 * 
	 * @param componentId 
	 * 	Id do componente ao qual a mensagem será associada.
	 * @param msgSummaryKey
	 * 	Chave da mensagem a ser utilizada.
	 * @param msgSummaryArgs
	 * 	Argumentos da mensagem
	 */
	protected void addMessageWarn(String componentId, String msgSummaryKey, Object... msgSummaryArgs) {
		addMessageWarn(componentId, msgSummaryKey, null, msgSummaryArgs, null);
	}
	
	/**
	 * Adiciona uma mensagem de severidade FATAL ao {@link FacesContext}
	 * 
	 * @param componentId 
	 * 	Id do componente ao qual a mensagem será associada.
	 * @param msgSummaryKey
	 * 	Chave da mensagem a ser utilizada.
	 * @param msgDetailKey
	 * 	Chave do detalhamento a ser utilizado
	 * @param msgSummaryArgs
	 * 	Argumentos da mensagem
	 * @param msgDetailArgs
	 * 	Argumentos do detalhamento.
	 */
	protected void addMessageFatal(String componentId, String msgSummaryKey, String msgDetailKey, Object[] msgSummaryArgs,
			Object[] msgDetailArgs) {
		addMessage(componentId, FacesMessage.SEVERITY_FATAL, msgSummaryKey, msgDetailKey, msgSummaryArgs, msgDetailArgs);
	}
	
	/**
	 * Adiciona uma mensagem de severidade FATAL ao {@link FacesContext}
	 * 
	 * @param componentId 
	 * 	Id do componente ao qual a mensagem será associada.
	 * @param msgSummaryKey
	 * 	Chave da mensagem a ser utilizada.
	 * @param msgSummaryArgs
	 * 	Argumentos da mensagem
	 */
	protected void addMessageFatal(String componentId, String msgSummaryKey, Object... msgSummaryArgs) {
		addMessageFatal(componentId, msgSummaryKey, null, msgSummaryArgs, null);
	}
	
	/**
	 * Remove as mensagens contidas no {@link FacesContext}
	 */
	public void removeMessages() {
		getFacesContext().getMessageList().clear();
	}
	
	/**
	 * Recupera um componente a partir de um id e o seu componente pai.
	 * 
	 * @param parent
	 *            O componente pai.
	 * @param componentId
	 *            O id do componente a ser procurado.
	 * @return O componente encontrado.
	 */
	protected UIComponent findComponent(UIComponent parent, String componentId) {
		UIComponent component = null;

		if (!componentId.equals(parent.getId())) {
			Iterator<UIComponent> children = parent.getFacetsAndChildren();
			for (Iterator<UIComponent> iterator = children; iterator.hasNext()
					&& (component == null);) {
				UIComponent child = iterator.next();
				component = findComponent(child, componentId);
			}
		} else {
			component = parent;
		}
		return component;
	}
	
	/**
	 * Adiciona um {@link Cookie} no cliente.
	 * 
	 * @param key
	 * 	Chave de identificação do {@link Cookie}
	 * @param value
	 * 	Valor que será armazenado no {@link Cookie}
	 */
	protected void addCookie(String key, String value) {
		HttpServletResponse response = (HttpServletResponse) getExternalContext().getResponse();
		Cookie cookie = new Cookie(key, value);
        response.addCookie(cookie);
    }

    /**
     * Recupera um cookie no cliente.
     *
     * @param key
     *	Chave de identificação do {@link Cookie}
     * @return
     * 	O valor armazenado no {@link Cookie} ou null se não localizado.
     */
    protected String getCookieValue(String key) {
        String result = null;
        HttpServletRequest request = (HttpServletRequest) getExternalContext().getRequest();
        Cookie cookie[] = request.getCookies();
        if (cookie != null) {
            for (int i = 0; i < cookie.length; i++) {
                if (cookie[i].getName().equalsIgnoreCase(key)) {
                    result = cookie[i].getValue();
                }
            }
        }
        return result;
    }

}
